<?php

/**
 * Uses rules embedded in a controller as permissions store.
 *
 * @author     Mon Zafra <monzee at gmail>
 * @copyright  (c)2009 Mon Zafra
 * @category   Mz
 * @package    modules
 * @subpackage admin
 * @license    http://mz-project.googlecode.com/svn/trunk/LICENSE    MIT License
 * @version    SVN: $Id: Embedded.php 43 2009-05-18 11:54:11Z monzee $
 */
class Admin_Model_Acl_Embedded extends Admin_Model_Acl_Array
{
    protected $_controller;
    protected $_accessManager;

    /**
     * Constructor.
     *
     * Same possible options as Admin_Model_Acl_Array, plus:
     * - controller Zend_Controller_Action Can't be passed via Zend_Config
     *
     * @param array|Zend_Config $options
     */
    public function __construct($options = array())
    {
        parent::__construct($options);
        $this->info = <<<INFO
This adapter uses the same user, roles, user roles and role tree data as
<code>Admin_Model_Acl_Array</code>. The permissions stored as public properties
of the controller. As such, this adapter can store permissions for one
resource only. Example rule definition:
<pre>
public \$allow = array(
    'admin', // allow access to all actions; same as 'admin' => array()
    'guest' => 'index', // allow access to indexAction; same as 'guest' => array('index')
    'author', // allowed all at first but denied at some action later (see below)
    'editor' => 'delete', // override the inherited deny rule below
);

public \$deny = array(
    'author' => 'delete', // author is allowed all actions except deleteAction
);

public \$allowUsers = array(
    'monzee', // monzee is allowed everywhere regardless of his role(s)
);

public \$denyUsers = array(
    'suspendedAuthor' => array('new', 'edit') // this author is not allowed to
                                              // write a new post or edit
);
</pre>
If at least one rule was found in the controller, every role is denied
access by default. You cannot allow access to everyone and deny specific users
or roles by declaring deny rules only.
INFO;
    }

    public function setController(Zend_Controller_Action $controller)
    {
        $this->_controller = $controller;
        return;
    }

    public function getController()
    {
        if (null === $this->_controller) {
            $this->_controller = $this->getAccessManager()->getActionController();
        }
        return $this->_controller;
    }

    public function getAccessManager()
    {
        if (null === $this->_accessManager) {
            $this->_accessManager =
                Zend_Controller_Action_HelperBroker::getStaticHelper('AccessManager');
        }
        return $this->_accessManager;
    }

    public function getAllPermissions()
    {
        // only holds permission for one controller at a time
        if (null === $this->_permissions) {
            $this->_permissions = $this->parseRules();
        }
        return $this->_permissions;
    }

    public function parseRules(Zend_Controller_Action $controller = null)
    {
        if (null === $controller) {
            $controller = $this->getController();
        }

        // the resource id isn't really necessary. i'm just doing this to make
        // this adapter compatible to the acl tester controller
        if (isset($controller->resourceId)) {
            $resId = $controller->resourceId;
        } else {
            list($resId) = $this->getAccessManager()->getAclModel()->getResource();
        }

        $rules = array();

        if (isset($controller->allow)) {
            $this->_parse($rules, $controller->allow, $resId);
        }

        if (isset($controller->allowUsers)) {
            $this->_parse($rules, $controller->allowUsers, $resId, true, true);
        }

        if (isset($controller->deny)) {
            $this->_parse($rules, $controller->deny, $resId, false);
        }

        if (isset($controller->denyUsers)) {
            $this->_parse($rules, $controller->denyUsers, $resId, false, true);
        }

        return $rules;
    }

    protected function _parse(&$rules, $src, $resId, $allowed = true, $userAsRole = false)
    {
        $src = (array) $src;
        foreach ($src as $key => $val) {
            if (is_int($key)) {
                $role = $userAsRole ? Admin_Helper_AccessManager::usernameAsRole($val)
                                    : $val;
                $priv = null;
            } else {
                $role = $userAsRole ? Admin_Helper_AccessManager::usernameAsRole($key)
                                    : $key;
                $priv = empty($val) ? null : $val;
            }
            $rules[] = array(
                'resource_id' => $resId,
                'role_id' => $role,
                'privilege' => $priv,
                'allowed' => $allowed
            );
        }
    }
}

